use crate::error::{ExpansionError, Result};
use crate::manifest::ActivationEvent;
use crate::services::context_manager::{Disposer, ExpansionContext};
use crate::services::event_manager::ProcessContributionsEvent;
use crate::services::helper::make_priority_sequence;
use crate::services::when_eval::{eval_when, ActivationContext};
use crate::services::{IExpansionImplement, IManifestManager};
use crate::system::Services;
use parking_lot::RwLock;
use std::any::Any;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;

pub trait IExtensionManager {}

pub struct ExtensionManager {
    implementations: HashMap<String, Box<dyn IExpansionImplement>>,
    activated: HashSet<String>,
    apis: Arc<RwLock<HashMap<String, Box<dyn Any + Send + Sync>>>>,
    disposers: HashMap<String, Arc<RwLock<Vec<Disposer>>>>,
}

impl ExtensionManager {
    pub fn new(implementations: HashMap<String, Box<dyn IExpansionImplement>>) -> Self {
        Self {
            implementations,
            activated: HashSet::new(),
            apis: Arc::new(RwLock::new(HashMap::new())),
            disposers: HashMap::new(),
        }
    }

    pub fn activate_with_deps(
        &mut self,
        event: &str,
        manifest_manager: &dyn IManifestManager,
        activation_context: &ActivationContext,
        services: Arc<Services>,
    ) -> Result<()> {
        let manifests_to_activate: Vec<_> = manifest_manager
            .get_should_activates(event)
            .into_iter()
            .filter(|m| {
                if self.activated.contains(&m.name) { return false; }
                m.activation_events.iter().find(|e| e.is_match(event)).map_or(false, |e| match e {
                    ActivationEvent::Detailed(d) if d.when.is_some() => eval_when(d.when.as_ref().unwrap(), activation_context),
                    _ => true,
                })
            })
            .collect();

        if manifests_to_activate.is_empty() { return Ok(()); }

        let batches = make_priority_sequence(&manifests_to_activate, &self.activated).map_err(ExpansionError::CyclicDependency)?;
        let mut activated_in_this_run = Vec::new();

        for batch in batches {
            for manifest in batch {
                let plugin_impl = self.implementations.get(&manifest.name).ok_or_else(|| ExpansionError::PluginNotRegistered(manifest.name.clone()))?;
                let disposers_arc = Arc::new(RwLock::new(Vec::new()));

                let context = ExpansionContext::new(manifest.name.clone(), services.clone(), disposers_arc.clone(), self.apis.clone());

                plugin_impl.before_activate(&context);

                let event_payload = ProcessContributionsEvent {
                    contributor_name: manifest.name.clone(),
                    contributes: manifest.contributes.clone().unwrap_or_default(),
                    contribution_points: manifest.contribution_points.clone().unwrap_or_default(),
                };
                services.event_manager.read().dispatch(&event_payload);

                if let Some(api) = plugin_impl.activate(context.clone()) {
                    self.apis.write().insert(manifest.name.clone(), api);
                }
                self.activated.insert(manifest.name.clone());
                self.disposers.insert(manifest.name.clone(), disposers_arc);
                activated_in_this_run.push(manifest.name.clone());
            }
        }

        for name in &activated_in_this_run {
            let plugin_impl = self.implementations.get(name.as_str()).unwrap();
            let disposers_arc = self.disposers.get(name.as_str()).unwrap().clone();
            let context = ExpansionContext::new(name.clone(), services.clone(), disposers_arc, self.apis.clone());
            plugin_impl.post_activated(&context);
        }

        Ok(())
    }

    pub fn deactivate_with_deps(&mut self, event: &str, manifest_manager: &dyn IManifestManager) -> Result<()> {
        let manifests_to_deactivate: Vec<_> = manifest_manager.get_should_deactivates(event).into_iter().filter(|m| self.activated.contains(&m.name)).collect();
        if manifests_to_deactivate.is_empty() { return Ok(()); }

        let mut batches = make_priority_sequence(&manifests_to_deactivate, &HashSet::new()).map_err(ExpansionError::CyclicDependency)?;
        batches.reverse();

        for batch in batches {
            for manifest in batch {
                if let Some(plugin_impl) = self.implementations.get(&manifest.name) {
                    plugin_impl.deactivate();
                }
                if let Some(disposers_arc) = self.disposers.remove(&manifest.name) {
                    let disposers = std::mem::take(&mut *disposers_arc.write());
                    for disposer in disposers {
                        disposer();
                    }
                }
                self.apis.write().remove(&manifest.name);
                self.activated.remove(&manifest.name);
            }
        }
        Ok(())
    }
}

impl IExtensionManager for ExtensionManager {}